#pragma once
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <memory>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// #include <sys/types.h>
// #include <sys/ipc.h>
#include <cstdlib>

enum
{
    GetKey_Err = 1,
    GetSem_Err,
    InitSem_Err
};

// 获取信号量的方式 flag
#define BUILD_SEM (IPC_CREAT | IPC_EXCL)
#define GET_SEM (IPC_CREAT)

///////////////////////////基于建造者模式的systemV信号量封装///////////
static std::string intTohex(int val)
{
    char buff[64] = {0};
    sprintf(buff, "%x", val);
    return buff;
}

class Semaphore // 信号量集 — 产品
{
public:
    Semaphore(int id)
        : _semid(id)
    {
    }
    ~Semaphore()
    {
        // 析构，将信号量集删除
        if (_semid > 0)
        {
            // 有效的信号量集
            int n = semctl(_semid, 0, IPC_RMID); // 0是忽略个数，全部删除
            if (n < 0)
            {
                std::cerr << _semid << " rm fail..." << std::endl;
            }
            std::cout << "Semaphore remove success" << std::endl;
        }
    }
    int GetId() const // 获取信号量集的id
    {
        return _semid;
    }
    void P(int index)
    {
        PV(index, -1);
    }
    void V(int index)
    {
        PV(index, 1);
    }

private:
    // 对PV操作进行合理管理
    void PV(int index, int data)
    {
        struct sembuf sem_buf;
        sem_buf.sem_num = index;    // 对index位置的信号量进行处理
        sem_buf.sem_flg = SEM_UNDO; // 不关心
        sem_buf.sem_op = data;      // 操作的数据量
        int n = semop(_semid, &sem_buf, 1);
        if (n < 0)
        {
            std::cerr << "semset PV fail..." << std::endl;
        }
    }

private:
    int _semid; // 信号集编号
};

// 产品的“构造工序”交给建造者
// 产品的“构造指挥工作”交给只会者
class SemaphoreBuilder // 接口类,由真正的建造者继承
{
public:
    virtual ~SemaphoreBuilder() {};
    virtual void GetKey() = 0;                         // 获取key值
    virtual void SetPermission(int perm) = 0;          // 设置权限
    virtual void SetSemNum(int n) = 0;                 // 设置信号集中信号量的个数
    virtual void SetInitVal(std::vector<int> val) = 0; // 设置信号量的值
    virtual void Build(int flag) = 0;                  // 建造者工作
    virtual void InitSemaphore() = 0;                  // 初始化某一个信号量集
    virtual const std::shared_ptr<Semaphore> &GetSemaPhore() = 0;  // 获取信号量集（获取商品）
};

// 用于构建key
const std::string Key_Pathname = "/tmp";
const int Key_ProjId = 0x77;

class ConcreateSemaphoreBuilder : public SemaphoreBuilder // 真正的建造者
{
public:
    virtual ~ConcreateSemaphoreBuilder() override {};
    virtual void GetKey() override
    {
        // 1、获取key
        //  接口：ftok
        key_t key = ftok(Key_Pathname.c_str(), Key_ProjId);
        if (key < 0) // key<0说明构建key值失败
        {
            std::cerr << "ftok fail..." << std::endl;
            exit(GetKey_Err);
        }
        std::cout << "ftok success: " << intTohex(key) << std::endl;
        // 创建key值成功
        _key = key;
    }
    virtual void SetPermission(int perm) override
    {
        //std::cout << perm << " " << _perm << std::endl;
        _perm = perm;
        //std::cout << perm << " " << _perm << std::endl;
    }
    virtual void SetSemNum(int n) override
    {
        _num = n;
    }
    virtual void SetInitVal(std::vector<int> val) override
    {
        _setval = std::move(val);
    }
    virtual void Build(int flag) override
    {
        // 1、创建/获取信号量集 semget
        int semid = semget(_key, _num, flag | _perm);
        if (semid < 0)
        {
            std::cerr << "semget fail..." << std::endl;
            exit(GetSem_Err);
        }
        // 创建/获取信号量集成功
        _semptr = std::make_shared<Semaphore>(semid); // 为信号量集创建对象
    }
    virtual void InitSemaphore() override
    {
        // 初始化信号量集：利用vector中的值为每一个信号量进行设置初始值
        if (_num > 0 && _setval.size() == (size_t)_num) // 判断是否设置的值合法
        {
            for (size_t i = 0; i < _num; ++i)
            {
                if (!InitSem(_semptr->GetId(), i, _setval[i]))
                {
                    std::cerr << "set initsem " << i << " fail..." << std::endl;
                    exit(InitSem_Err);
                }
            }
        }
        else
        {
            // 出错
        }
    }

    const std::shared_ptr<Semaphore> &GetSemaPhore() override
    {
        return _semptr;
    }

private:
    bool InitSem(int semid, int index, int val)
    {
        union semun
        {
            int val;               /* Value for SETVAL */
            struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
            unsigned short *array; /* Array for GETALL, SETALL */
            struct seminfo *__buf; /* Buffer for IPC_INFO
                                      (Linux-specific) */
        } un;
        un.val = val;
        // 设置信号集初始值
        //std::cout << semid << " " << _semptr->GetId() << std::endl;
        int n = semctl(semid, index, SETVAL, un);
        if (n < 0)
        {
            std::cerr << "SETVAL fail..." << std::endl;
            return false;
        }
        // 设置初始值成功
        return true;
    }

private:
    key_t _key = 0;                     // key值
    int _perm = 0x000;                  // 权限
    int _num = 0;                       // 信号集中信号量个数
    std::vector<int> _setval;           // 设置信号集中的信号量
    std::shared_ptr<Semaphore> _semptr; // 指向产品的指针
};

const int defaultperm = 0666;
const std::vector<int> defaultvalset = {1};
const int defaultnum = 1;

class Director // 指挥者
{
public:
    Director()
    {
    }
    void Concreate(std::shared_ptr<SemaphoreBuilder> builder, int flag = GET_SEM, int perm = defaultperm, const std::vector<int> &valset = defaultvalset, int num = defaultnum) // 指挥者建造商品
    {
        builder->GetKey();
        builder->SetPermission(perm);
        builder->SetSemNum(num);
        builder->SetInitVal(valset);
        // 准备工作完成
        builder->Build(flag);
        if (flag == BUILD_SEM) // 如果是第一次创建集合，那么就需要初始化信号量集合中的初始值
            builder->InitSemaphore();
    }
};
